package com.ihakul.logger;

import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Logger;

import static com.ihakul.logger.LogBuilder.LOG_FILE_PATH;

/**
 * 默认的配置方案：日志显示级别为{@code INFO}及以上并记录到文件，目录位于
 * {@link LogBuilder#LOG_FILE_PATH LOG_FILE_PATH}下，内容连续追加到文件，单个文件最大{@code 500KB}，最多
 * 保留最近的{@code 10 * 500KB} 记录。
 *
 * @author Aaron
 * @version 2.1
 * Create at 2019/7/17
 */
final class Configurator {
    private static final String PREFIX = "logging";
    private static final String CONFIGURE = PREFIX+".properties";
    private static final String FORMAT = "%1$tF\t%1$tT.%1$tL\t%3$s\t%2$s\t%4$s%n";

    private final boolean readConfiguration;
    private final Context mContext;
    /*package*/String buffering="false";

    private RecordFilter mFilter;
    private SevereReporter mReporter;

    /**See {@link Configurator}**/
    Configurator(Context context,boolean readConfiguration) {
        mContext =context;
        this.readConfiguration=readConfiguration;
    }

    void init(CrashCatcher catcher)throws IOException {

        String pattern="runtime.log.%g";
        String format=FORMAT;   // log formatter
        String limit="512000";  // max. 500 KB
        String count="10";      // 10 files rotated
        String append="true";   // appending
        String buffering=this.buffering;// buffering file

        File rootDir;

        if ((rootDir=getPath()) == null) {
            throw new IOException("Need an external storage device to work!");
        }

        String packageName=mContext.getPackageName();
        final File outputDir = new File(rootDir,
                PREFIX.concat(File.separator).concat(packageName)
        );
        if(readConfiguration){
            Properties props=new Properties();
            try (InputStream in = mContext.getClassLoader().getResourceAsStream(CONFIGURE)) {
                BufferedInputStream bin = new BufferedInputStream(in);
                props.load(bin);
                pattern=props.getProperty(PREFIX+".file.pattern",pattern);
                format=props.getProperty(PREFIX+".file.format",format);
                limit=props.getProperty(PREFIX+".file.limit",limit);
                count=props.getProperty(PREFIX+".file.count",count);
                append=props.getProperty(PREFIX+".file.append",append);
                buffering=props.getProperty(PREFIX+".file.buffering",buffering);

                int index;
                if ((index=pattern.lastIndexOf('/'))!=-1) {
                    if (index==0||index==pattern.length()){
                        throw new IOException("Correct the value of the'pattern'field please");
                    }

                    if (!mkdirs(new File( rootDir, pattern.substring(
                            0, index)))){
                        throw new IOException("Log output directory creation failed");
                    }
                }

                if (mkdirs(outputDir))
                this.initConfig(new File(outputDir,pattern),
                        format,
                        Integer.parseInt(limit),
                        Integer.parseInt(count),
                        Boolean.parseBoolean(append),
                        Boolean.parseBoolean(buffering),
                        catcher);
            }

        }else
        {

            if (mkdirs(outputDir))
           this.initConfig(new File(outputDir,pattern),
                   format,
                   Integer.parseInt(limit),
                   Integer.parseInt(count),
                   Boolean.parseBoolean(append),
                   Boolean.parseBoolean(buffering),
                   catcher);
        }
    }

    RecordFilter getHandler() {
        if (mFilter != null) {
            return mFilter;
        }else
        {
            return new RecordFilter(
              new FileOutputFormatter(FORMAT)
            );
        }
    }

    void setReporter(SevereReporter reporter) {
        mReporter=reporter;
    }

    private boolean mkdirs(File file) {
        if (!file.exists()) {
            return file.mkdirs();
        }
        return true;
    }

    private File getPath(){
        if (!Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState())) {
            return null;
        }

        if(TextUtils.isEmpty(LOG_FILE_PATH)){
            return  Environment.getExternalStorageDirectory();
        }

        File dir = new File(Environment.getExternalStorageDirectory(),
                LOG_FILE_PATH);
        if (!mkdirs(dir)) {
            return null;
        }
        return dir;
    }

    private void initConfig(File pattern,
                            String format,
                            int limit,
                            int count,
                            boolean appending,
                            boolean buffering,
                            CrashCatcher catcher)
            throws IOException {

        Logger root=IotLogger.rootLogger();
        Handler[] handlers=root.getHandlers();

        if (handlers.length == 1&&
                IotLogger.ANDROID_HANDLER.equals(handlers[0].getClass().getName())) {

            // Create new log file handler (max. {limit}, {count} files rotated, and appending)
            Handler fileHandler = createFileHandler(pattern.getPath(),
                    limit,
                    count,
                    appending,
                    buffering);

            // Set log message formatter
            final Formatter formatter=new FileOutputFormatter(format);
            fileHandler.setFormatter(formatter);

            // Set log Filter for error, transfer over the network to the server
            this.mFilter=new RecordFilter(formatter);
            fileHandler.setFilter(mFilter);
            fileHandler.setErrorManager(mFilter);

            mFilter.addReporter(mReporter);

            if(catcher!=null) {
                catcher.mHandler =mFilter;
            }

            // add file logging ...
            root.addHandler(fileHandler);
        }
    }

    private Handler createFileHandler(String path, int limit, int count, boolean appending, final boolean buffering)
            throws IOException {

        return buffering?new BufferedFileHandler(path,
                limit,
                count,
                appending):
                new FileHandler(path,
                        limit,
                        count,
                        appending);
    }

}
